package com.ruoyi.pack.service.impl;

import java.util.List;
import java.util.Date;
import java.math.BigDecimal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.AmapUtils;
import com.ruoyi.pack.mapper.PackLocationLogMapper;
import com.ruoyi.pack.mapper.PackLocatorMapper;
import com.ruoyi.pack.mapper.PackContainerMapper;
import com.ruoyi.pack.mapper.PackWarehouseFenceMapper;
import com.ruoyi.pack.domain.PackLocationLog;
import com.ruoyi.pack.domain.PackLocator;
import com.ruoyi.pack.domain.PackContainer;
import com.ruoyi.pack.domain.PackWarehouseFence;
import com.ruoyi.pack.domain.dto.PackContainerWarehouseInfoDTO;
import com.ruoyi.pack.service.IPackLocationLogService;
import com.ruoyi.pack.service.IPackContainerService;
import com.ruoyi.pack.service.IPackTransferRecordService;

/**
 * 定位日志Service业务层处理
 * 
 * @author ruoyi
 * @date 2024-01-01
 */
@Service
public class PackLocationLogServiceImpl implements IPackLocationLogService 
{
    private static final Logger logger = LoggerFactory.getLogger(PackLocationLogServiceImpl.class);
    
    @Autowired
    private PackLocationLogMapper packLocationLogMapper;
    
    @Autowired
    private PackLocatorMapper packLocatorMapper;
    
    @Autowired
    private PackContainerMapper packContainerMapper;
    
    @Autowired
    private PackWarehouseFenceMapper packWarehouseFenceMapper;
    
    @Autowired
    private IPackContainerService packContainerService;
    
    @Autowired
    private IPackTransferRecordService packTransferRecordService;
    
    @Autowired
    private AmapUtils amapUtils;

    /**
     * 查询定位日志
     * 
     * @param logId 定位日志主键
     * @return 定位日志
     */
    @Override
    public PackLocationLog selectPackLocationLogByLogId(Long logId)
    {
        return packLocationLogMapper.selectPackLocationLogByLogId(logId);
    }

    /**
     * 查询定位日志列表
     * 
     * @param packLocationLog 定位日志
     * @return 定位日志
     */
    @Override
    public List<PackLocationLog> selectPackLocationLogList(PackLocationLog packLocationLog)
    {
        return packLocationLogMapper.selectPackLocationLogList(packLocationLog);
    }

    /**
     * 新增定位日志
     * 
     * @param packLocationLog 定位日志
     * @return 结果
     */
    @Override
    @Transactional
    public int insertPackLocationLog(PackLocationLog packLocationLog)
    {
        // 设置上传时间为当前时间（如果未提供）
        if (packLocationLog.getUploadTime() == null)
        {
            packLocationLog.setUploadTime(DateUtils.getNowDate());
        }
        
        packLocationLog.setCreateTime(DateUtils.getNowDate());
        packLocationLog.setCreateBy(SecurityUtils.getUsername());
        
        // 设置删除标志
        packLocationLog.setDelFlag("0");
        
        // 先插入定位日志
        int result = packLocationLogMapper.insertPackLocationLog(packLocationLog);
        
        // 在插入定位日志之后，更新定位器和器具的相关信息
        if (result > 0 && packLocationLog.getLocatorId() != null)
        {


            // 1. 更新定位器状态
            updateLocatorStatus(packLocationLog);
            
            // 2. 更新器具位置信息（如果定位器绑定了器具）
            if (packLocationLog.getContainerId() != null)
            {
                updateContainerLocationInfo(packLocationLog);
            }
        }
        
        return result;
    }

    /**
     * 修改定位日志
     * 
     * @param packLocationLog 定位日志
     * @return 结果
     */
    @Override
    public int updatePackLocationLog(PackLocationLog packLocationLog)
    {
        packLocationLog.setUpdateTime(DateUtils.getNowDate());
        packLocationLog.setUpdateBy(SecurityUtils.getUsername());
        return packLocationLogMapper.updatePackLocationLog(packLocationLog);
    }

    /**
     * 批量删除定位日志
     * 
     * @param logIds 需要删除的定位日志主键
     * @return 结果
     */
    @Override
    public int deletePackLocationLogByLogIds(Long[] logIds)
    {
        return packLocationLogMapper.deletePackLocationLogByLogIds(logIds);
    }

    /**
     * 删除定位日志信息
     * 
     * @param logId 定位日志主键
     * @return 结果
     */
    @Override
    public int deletePackLocationLogByLogId(Long logId)
    {
        return packLocationLogMapper.deletePackLocationLogByLogId(logId);
    }
    
    /**
     * 根据定位器ID查询最新的定位日志
     * 
     * @param locatorId 定位器ID
     * @return 定位日志
     */
    @Override
    public PackLocationLog selectLatestLogByLocatorId(Long locatorId)
    {
        return packLocationLogMapper.selectLatestLogByLocatorId(locatorId);
    }
    
    /**
     * 根据器具ID查询最新的定位日志
     * 
     * @param containerId 器具ID
     * @return 定位日志
     */
    @Override
    public PackLocationLog selectLatestLogByContainerId(Long containerId)
    {
        return packLocationLogMapper.selectLatestLogByContainerId(containerId);
    }
    
    /**
     * 根据条件查询定位日志列表（高级筛选）
     * 
     * @param locatorCode 定位器编码
     * @param containerCode 器具编码
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @param minBatteryLevel 最小电池电量
     * @param maxBatteryLevel 最大电池电量
     * @param minTemperature 最小温度
     * @param maxTemperature 最大温度
     * @param shockLevel 震动等级
     * @param uploadAddress 上传地址
     * @return 定位日志集合
     */
    @Override
    public List<PackLocationLog> selectPackLocationLogByCondition(
        String locatorCode,
        String containerCode,
        String startTime,
        String endTime,
        Integer minBatteryLevel,
        Integer maxBatteryLevel,
        BigDecimal minTemperature,
        BigDecimal maxTemperature,
        Integer shockLevel,
        String uploadAddress
    )
    {
        return packLocationLogMapper.selectPackLocationLogByCondition(
            locatorCode, containerCode, startTime, endTime, 
            minBatteryLevel, maxBatteryLevel, minTemperature, maxTemperature,
            shockLevel, uploadAddress);
    }
    
    /**
     * 上传定位数据（设备接口）
     * 
     * @param packLocationLog 定位日志
     * @return 结果
     */
    @Override
    @Transactional
    public int uploadLocationData(PackLocationLog packLocationLog)
    {
        // 1. 验证定位器是否存在
        PackLocator locator = packLocatorMapper.selectPackLocatorByLocatorId(packLocationLog.getLocatorId());
        if (locator == null)
        {
            return 0;
        }
        
        // 2. 设置上传时间为当前时间
        packLocationLog.setUploadTime(DateUtils.getNowDate());
        
        // 3. 设置器具ID（如果定位器已绑定器具）
        if (locator.getContainerId() != null)
        {
            packLocationLog.setContainerId(locator.getContainerId());
        }
        
        // 4. 设置创建者信息
        String username = "system";
        try {
            username = SecurityUtils.getUsername();
        } catch (Exception e) {
            // 设备上传可能没有登录用户，使用默认值
        }
        packLocationLog.setCreateBy(username);
        packLocationLog.setCreateTime(DateUtils.getNowDate());
        
        // 5. 设置删除标志
        packLocationLog.setDelFlag("0");
        
        // 6. 保存日志
        int result = packLocationLogMapper.insertPackLocationLog(packLocationLog);
        
        // 7. 更新定位器状态
        if (result > 0 && packLocationLog.getBatteryLevel() != null)
        {
            packLocatorMapper.updateLocatorStatus(
                packLocationLog.getLocatorId(),
                "1", // 正常状态
                packLocationLog.getBatteryLevel().longValue(),
                null // 不更新信号强度
            );
        }
        
        return result;
    }
    
    /**
     * 更新定位器状态
     * 
     * @param packLocationLog 定位日志
     */
    private void updateLocatorStatus(PackLocationLog packLocationLog)
    {
        // 设置定位器状态为正常（表示设备在线）
        String locatorStatus = "1";
        
        // 电池电量
        Long batteryLevel = packLocationLog.getBatteryLevel() != null ? 
            packLocationLog.getBatteryLevel().longValue() : null;
        
        // 信号强度（如果有传感器数据可以从中解析）
        Long signalStrength = null;
        
        // 更新定位器状态（同时会更新最后在线时间）
        packLocatorMapper.updateLocatorStatus(
            packLocationLog.getLocatorId(),
            locatorStatus,
            batteryLevel,
            signalStrength
        );
    }
    
    /**
     * 更新器具位置信息
     * 
     * @param packLocationLog 定位日志
     */
    private void updateContainerLocationInfo(PackLocationLog packLocationLog)
    {
        // 转换位置信息
        String latitude = packLocationLog.getLatitude() != null ? 
            packLocationLog.getLatitude().toString() : null;
        String longitude = packLocationLog.getLongitude() != null ? 
            packLocationLog.getLongitude().toString() : null;
        String uploadTime = packLocationLog.getUploadTime() != null ? 
            DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, packLocationLog.getUploadTime()) : null;
        
        // 调用高德地图API进行逆地址解析
        String province = null;
        String city = null;
        String district = null;
        String detailAddress = packLocationLog.getUploadAddress();
        
        if (packLocationLog.getLatitude() != null && packLocationLog.getLongitude() != null)
        {
            try {
                java.util.Map<String, String> addressInfo = amapUtils.getAddressFromCoordinate(
                    packLocationLog.getLongitude(), 
                    packLocationLog.getLatitude()
                );
                if (addressInfo != null && !addressInfo.isEmpty())
                {
                    province = addressInfo.get("province");
                    city = addressInfo.get("city");
                    district = addressInfo.get("district");
                    
                    // 如果详细地址为空，使用高德返回的地址
                    if (StringUtils.isEmpty(detailAddress))
                    {
                        detailAddress = addressInfo.get("address");
                    }
                }
            } catch (Exception e) {
                // 如果高德API调用失败，记录日志但不影响主流程
                logger.warn("调用高德地图API失败，继续使用原有地址信息: {}", e.getMessage());
            }
        }
        
        // 更新器具位置信息
        packContainerMapper.updateContainerLocationInfo(
            packLocationLog.getContainerId(),
            latitude,
            longitude,
            uploadTime,
            packLocationLog.getLogId(), // 将在插入后获得
            detailAddress,
            province,
            city,
            district
        );
        
        // 查询器具可能进入的仓库信息，并判断是否在某个仓库围栏内
        PackContainerWarehouseInfoDTO currentWarehouse = null;
        try {
            // 首先获取器具信息
            PackContainer container = packContainerMapper.selectPackContainerByContainerId(packLocationLog.getContainerId());
            if (container != null) {
                // 获取器具原有的位置状态和仓库信息
                String originalLocationStatus = container.getLocationStatus();
                Long originalWarehouseId = container.getCurrentWarehouseId();
                
                System.out.println("=== 器具[" + container.getContainerCode() + "]位置状态分析 ===");
                System.out.println("原位置状态: " + ("1".equals(originalLocationStatus) ? "在库" : "在途"));
                System.out.println("原所在仓库ID: " + originalWarehouseId);
                
                // 查询器具可能进入的仓库信息
                List<PackContainerWarehouseInfoDTO> possibleWarehouses = packContainerMapper.selectContainerPossibleWarehouses(
                    container.getCompanyId(),           // 器具归属公司ID
                    container.getMaterialCode(),        // 器具型号编码
                    container.getCurrentMaterialCode()  // 当前物料编码
                );
                
                if (possibleWarehouses != null && !possibleWarehouses.isEmpty()) {
                    // 获取当前定位器的经纬度
                    BigDecimal currentLat = packLocationLog.getLatitude();
                    BigDecimal currentLng = packLocationLog.getLongitude();
                    
                    if (currentLat != null && currentLng != null) {
                        // 遍历所有可能的仓库，检查是否在围栏内
                        for (PackContainerWarehouseInfoDTO warehouseInfo : possibleWarehouses) {
                            // 查询仓库电子围栏信息
                            List<PackWarehouseFence> fenceList = packWarehouseFenceMapper.selectPackWarehouseFenceByWarehouseId(warehouseInfo.getWarehouseId());
                            warehouseInfo.setFenceList(fenceList);
                            
                            // 判断当前坐标是否在该仓库的围栏内
                            boolean isInsideFence = isPointInsideWarehouseFence(currentLat, currentLng, fenceList);
                            
                            // 如果在围栏内，记录当前仓库
                            if (isInsideFence && currentWarehouse == null) {
                                currentWarehouse = warehouseInfo;
                            }
                        }
                    }
                }
                
                // 根据6种情况进行处理
                processContainerLocationChange(container, originalLocationStatus, originalWarehouseId, currentWarehouse, packLocationLog);
                
            }
        } catch (Exception e) {
            // 查询失败不影响主流程，只记录日志
            logger.warn("查询器具可能进入的仓库信息失败: {}", e.getMessage(), e);
        }
    }
    
    /**
     * 处理器具位置变化的6种情况
     * 
     * @param container 器具信息
     * @param originalLocationStatus 原位置状态
     * @param originalWarehouseId 原仓库ID
     * @param currentWarehouse 当前检测到的仓库
     * @param packLocationLog 定位日志
     */
    private void processContainerLocationChange(PackContainer container, String originalLocationStatus, 
                                              Long originalWarehouseId, PackContainerWarehouseInfoDTO currentWarehouse, 
                                              PackLocationLog packLocationLog)
    {
        Long currentWarehouseId = currentWarehouse != null ? currentWarehouse.getWarehouseId() : null;
        String currentWarehouseName = currentWarehouse != null ? currentWarehouse.getWarehouseName() : null;
        
        System.out.println("当前检测到的仓库: " + (currentWarehouseName != null ? currentWarehouseName + "(ID:" + currentWarehouseId + ")" : "无"));
        
        // 判断处理情况
        if (currentWarehouseId != null && originalWarehouseId != null && currentWarehouseId.equals(originalWarehouseId)) {
            // 新定位库房与原库房相同
            if ("1".equals(originalLocationStatus)) {
                // 情况1：新定位的库房与器具原库房相同，并且器具原来处于在库状态
                System.out.println("=== 情况1：器具在原库房中没有移动，无需操作 ===");
                // 无动作
            } else {
                // 情况2：新定位的库房与器具原库房相同，并且器具原来处于在途状态
                System.out.println("=== 情况2：器具退回了原库房，执行入库操作 ===");
                performContainerInbound(container, currentWarehouse, packLocationLog);
            }
        } else if (currentWarehouseId != null && originalWarehouseId != null && !currentWarehouseId.equals(originalWarehouseId)) {
            // 新定位库房与原库房不同
            if ("1".equals(originalLocationStatus)) {
                // 情况3：新定位的库房与器具原库房不同，并且器具原来处于在库状态
                System.out.println("=== 情况3：器具从原库房直接挪到了新库房，执行出库+入库操作 ===");
                performContainerOutbound(container, originalWarehouseId, packLocationLog);
                performContainerInbound(container, currentWarehouse, packLocationLog);
            } else {
                // 情况4：新定位的库房与器具原库房不同，并且器具原来处于在途状态
                System.out.println("=== 情况4：器具从运输途中到了新库房，执行入库操作 ===");
                performContainerInbound(container, currentWarehouse, packLocationLog);
            }
        } else if (currentWarehouseId == null) {
            // 新定位器具不在任何库房
            if ("1".equals(originalLocationStatus)) {
                // 情况5：新定位器具不在任何库房，并且器具原来处于在库状态
                System.out.println("=== 情况5：器具从原库房到了运输途中，执行出库操作 ===");
                performContainerOutbound(container, originalWarehouseId, packLocationLog);
            } else {
                // 情况6：新定位器具不在任何库房，并且器具原来处于在途状态
                System.out.println("=== 情况6：器具一直处于运输途中，无需操作 ===");
                // 无动作
            }
        }
        
        System.out.println("=== 位置状态处理完成 ===");
    }
    
    /**
     * 执行器具出库操作
     * 
     * @param container 器具信息
     * @param sourceWarehouseId 来源仓库ID
     * @param packLocationLog 定位日志
     */
    private void performContainerOutbound(PackContainer container, Long sourceWarehouseId, PackLocationLog packLocationLog)
    {
        try {
            System.out.println("--- 执行器具出库操作 ---");
            
            // 1. 更新器具状态为在途
            packContainerService.updateContainerStatus(container.getContainerId(), "2", null, null);
            System.out.println("已更新器具状态为在途");
            
            // 2. 获取操作员姓名
            String operatorName = "系统自动";
            try {
                operatorName = SecurityUtils.getUsername();
            } catch (Exception e) {
                // 系统自动触发时可能没有登录用户
            }
            
            // 3. 创建器具移动记录（出库阶段）
            Long transferRecordId = packTransferRecordService.createOutboundRecord(
                container,
                sourceWarehouseId,
                null, // 来源仓库名称会在Service中查询
                null, // 目标仓库ID为空，表示不确定目标
                null, // 目标仓库名称为空
                packLocationLog.getLogId(),
                operatorName
            );
            
            if (transferRecordId != null) {
                System.out.println("成功创建移库记录（出库阶段），记录ID: " + transferRecordId);
                System.out.println("  - 器具ID: " + container.getContainerId());
                System.out.println("  - 器具编码: " + container.getContainerCode());
                System.out.println("  - 器具型号: " + container.getMaterialCode() + "/" + container.getMaterialName());
                System.out.println("  - 来源仓库ID: " + sourceWarehouseId);
                System.out.println("  - 移库类型: " + ("2".equals(container.getPackingStatus()) ? "满箱移库" : "空箱移库"));
                System.out.println("  - 移库阶段: 出库（在库→在途）");
                System.out.println("  - 触发定位日志ID: " + packLocationLog.getLogId());
                System.out.println("  - 自动生成: 是");
                System.out.println("  - 操作员: " + operatorName);
                
                if ("2".equals(container.getPackingStatus())) {
                    // 满箱移库时记录物料信息
                    System.out.println("  - 物料编码: " + container.getCurrentMaterialCode());
                    System.out.println("  - 物料名称: " + container.getCurrentMaterialName());
                    System.out.println("  - 物料数量: " + container.getCurrentMaterialQuantity());
                    System.out.println("  - 内衬方案: " + container.getCurrentLiningScheme());
                }
            } else {
                System.out.println("创建移库记录（出库阶段）失败");
            }
            
            System.out.println("器具出库操作完成");
            
        } catch (Exception e) {
            logger.error("器具出库操作失败: {}", e.getMessage(), e);
            System.out.println("器具出库操作失败: " + e.getMessage());
        }
    }
    
    /**
     * 执行器具入库操作
     * 
     * @param container 器具信息
     * @param targetWarehouse 目标仓库信息
     * @param packLocationLog 定位日志
     */
    private void performContainerInbound(PackContainer container, PackContainerWarehouseInfoDTO targetWarehouse, PackLocationLog packLocationLog)
    {
        try {
            System.out.println("--- 执行器具入库操作 ---");
            
            // 1. 更新器具状态为在库，更新所在仓库和项目
            packContainerService.updateContainerStatus(container.getContainerId(), "1", null, null);
            
            // 2. 更新器具的仓库和项目信息
            PackContainer updateContainer = new PackContainer();
            updateContainer.setContainerId(container.getContainerId());
            updateContainer.setCurrentWarehouseId(targetWarehouse.getWarehouseId());
            updateContainer.setCurrentProjectId(targetWarehouse.getProjectId());
            updateContainer.setCurrentProjectCode(targetWarehouse.getProjectCode());
            updateContainer.setCurrentProjectName(targetWarehouse.getProjectName());
            updateContainer.setUpdateTime(DateUtils.getNowDate());
            
            String operatorName = "系统自动";
            try {
                operatorName = SecurityUtils.getUsername();
                updateContainer.setUpdateBy(operatorName);
            } catch (Exception e) {
                updateContainer.setUpdateBy("系统自动");
            }
            
            packContainerMapper.updatePackContainer(updateContainer);
            System.out.println("已更新器具状态为在库，仓库ID: " + targetWarehouse.getWarehouseId() + "，项目: " + targetWarehouse.getProjectName());
            
            // 3. 更新移库记录（入库阶段）- 完成出库阶段创建的记录
            boolean updateSuccess = packTransferRecordService.updateToInboundRecord(
                container.getContainerId(),
                targetWarehouse.getWarehouseId(),
                targetWarehouse.getWarehouseName(),
                targetWarehouse.getProjectId(),
                targetWarehouse.getProjectCode(),
                targetWarehouse.getProjectName(),
                packLocationLog.getLogId(),
                operatorName
            );
            
            if (updateSuccess) {
                System.out.println("成功更新移库记录（入库阶段完成）");
                System.out.println("  - 器具ID: " + container.getContainerId());
                System.out.println("  - 器具编码: " + container.getContainerCode());
                System.out.println("  - 器具型号: " + container.getMaterialCode() + "/" + container.getMaterialName());
                System.out.println("  - 目标仓库: " + targetWarehouse.getWarehouseName() + "(ID:" + targetWarehouse.getWarehouseId() + ")");
                System.out.println("  - 目标项目: " + targetWarehouse.getProjectName() + "(" + targetWarehouse.getProjectCode() + ")");
                System.out.println("  - 移库类型: " + ("2".equals(container.getPackingStatus()) ? "满箱移库" : "空箱移库"));
                System.out.println("  - 移库阶段: 出库→入库（完成）");
                System.out.println("  - 入库定位日志ID: " + packLocationLog.getLogId());
                System.out.println("  - 自动完成: 是");
                System.out.println("  - 操作员: " + operatorName);
                
                if ("2".equals(container.getPackingStatus())) {
                    // 满箱移库时记录物料信息
                    System.out.println("  - 物料编码: " + container.getCurrentMaterialCode());
                    System.out.println("  - 物料名称: " + container.getCurrentMaterialName());
                    System.out.println("  - 物料数量: " + container.getCurrentMaterialQuantity());
                    System.out.println("  - 内衬方案: " + container.getCurrentLiningScheme());
                }
            } else {
                System.out.println("更新移库记录（入库阶段）失败 - 可能没有找到对应的出库记录");
            }
            
            System.out.println("器具入库操作完成");
            
        } catch (Exception e) {
            logger.error("器具入库操作失败: {}", e.getMessage(), e);
            System.out.println("器具入库操作失败: " + e.getMessage());
        }
    }
    
    /**
     * 判断点是否在仓库围栏内
     * 使用射线法（Ray Casting Algorithm）判断点是否在多边形内
     * 
     * @param pointLat 待判断点的纬度
     * @param pointLng 待判断点的经度
     * @param fenceList 围栏点列表
     * @return 是否在围栏内
     */
    private boolean isPointInsideWarehouseFence(BigDecimal pointLat, BigDecimal pointLng, List<PackWarehouseFence> fenceList)
    {
        if (fenceList == null || fenceList.isEmpty()) {
            return false;
        }
        
        // 按围栏类型和点序号排序，构建多边形
        // 目前只支持类型1（仓库边界）的围栏判断
        List<PackWarehouseFence> boundaryFences = fenceList.stream()
            .filter(fence -> "1".equals(fence.getFenceType()))
            .sorted((f1, f2) -> f1.getPointOrder().compareTo(f2.getPointOrder()))
            .collect(java.util.stream.Collectors.toList());
            
        if (boundaryFences.size() < 3) {
            // 少于3个点无法构成多边形
            return false;
        }
        
        // 使用射线法判断点是否在多边形内
        double x = pointLng.doubleValue();
        double y = pointLat.doubleValue();
        
        int intersectionCount = 0;
        int n = boundaryFences.size();
        
        for (int i = 0; i < n; i++) {
            int j = (i + 1) % n; // 下一个点的索引，最后一个点连接到第一个点
            
            double xi = boundaryFences.get(i).getLongitude().doubleValue();
            double yi = boundaryFences.get(i).getLatitude().doubleValue();
            double xj = boundaryFences.get(j).getLongitude().doubleValue();
            double yj = boundaryFences.get(j).getLatitude().doubleValue();
            
            // 检查射线是否与边相交
            if (((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi)) {
                intersectionCount++;
            }
        }
        
        // 奇数次相交表示点在多边形内
        return intersectionCount % 2 == 1;
    }
} 